#define FUSE_USE_VERSION 26

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fuse.h>
#include <signal.h>
#include <semaphore.h>
#include <pthread.h>

#include "includes/libFileSystem.h"
#include "includes/libSockets.h"
#include "includes/libCache.h"
#include "includes/leerArchivoConfiguracion.h"
#include "includes/libFuse.h"
#include "includes/libSincroDirYArch.h"
#include "includes/tiposFileSystem.h"
#include "includes/tiposSockets.h"
#include "includes/tiposCache.h"
#include "includes/tiposPaquetes.h"
#include "includes/tiposSincroDirYArch.h"
#include "includes/utils.h"
#include "includes/log.h"

int8_t imprimirDebug = 0;
int8_t usarFUSE = 0;
int8_t usarSockets = 1;
int8_t usarPoolConexiones = 1;

t_fat32_bootSector bootSector;
char IP_ACTIVA[NI_MAXHOST], PUERTO_ACTIVO[10];
char IP_DISCO[NI_MAXHOST], PUERTO_DISCO[10];
char IP_RAID1[NI_MAXHOST], PUERTO_RAID1[10];
int32_t MAX_CONEXIONES;
int32_t TAMANIO_CACHE;
t_conexion* poolConexiones;
t_nodoCache* pCacheFte;
t_nodoSincro* lSincro;
uint32_t* tablaFatEnMem;
int8_t primeraLecturaFat = 1;

pthread_mutex_t mutexUsandoFat = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexUsandoCache = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexUsandoPoolConexiones = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexUsandoListaSincro = PTHREAD_MUTEX_INITIALIZER;
sem_t semConexiones;

static int32_t FLAME_access(const char* path, int32_t mask) {
	int32_t res = FLAME_accessAux(path, mask);
	return res;
}

static int32_t FLAME_open(const char* path, struct fuse_file_info* fi) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_openAux(path, fi);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_openAux(path, fi);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}

static int32_t FLAME_readdir(char *path, void *buf, fuse_fill_dir_t filler,
		off_t offset, struct fuse_file_info *fi) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_readdirAux(path, buf, filler, offset, fi);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			res = FLAME_readdirAux(path, buf, filler, offset, fi);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	}
	return res;
}

static int32_t FLAME_read(const char* path, char* buf, size_t size,
		off_t offset, struct fuse_file_info* fi) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_readAux(path, buf, size, offset, fi);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_readAux(path, buf, size, offset, fi);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}

static int32_t FLAME_create(const char *path, mode_t modo,
		struct fuse_file_info *fInfo) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_createAux(path, modo, fInfo);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_createAux(path, modo, fInfo);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}

static int32_t FLAME_mkdir(const char *path, mode_t modo) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_mkdirAux(path, modo);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			res = FLAME_mkdirAux(path, modo);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	}
	return res;
}

static int32_t FLAME_rmdir(const char* path) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_rmdirAux(path);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - SUBDIRECTORIO
		{
			res = FLAME_rmdirAux(path);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - SUBDIRECTORIO
	}

	return res;
}

static int32_t FLAME_getattr(const char* path, struct stat* stbuf) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_getattrAux(path, stbuf);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_getattrAux(path, stbuf);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}
	return res;
}

static int32_t FLAME_flush(const char * path, struct fuse_file_info * info) {
	int32_t res = FLAME_flushAux(path, info);
	return res;
}

static int32_t FLAME_unlink(const char* path) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_unlinkAux(path);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_unlinkAux(path);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}
static int32_t FLAME_ftruncate(const char * path, off_t tamanio,
		struct fuse_file_info * info) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_ftruncateAux(path, tamanio, info);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_ftruncateAux(path, tamanio, info);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}

static int32_t FLAME_write(const char* path, char* buf, size_t size,
		off_t offset, struct fuse_file_info* fi) {
	int32_t res;

	if (strcmp((char*) path, "/") != 0) {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			char* rutaDir;
			parsearDir((char*) path, &rutaDir);
			empiezoATrabajarEnPath(rutaDir); //LOCK MUTEX - DIRECTORIO
			{
				res = FLAME_writeAux(path, buf, size, offset, fi);
			}
			dejoDeTrabajarEnPath(rutaDir); //UNLOCK MUTEX - DIRECTORIO
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	} else {
		empiezoATrabajarEnPath((char*) path); //LOCK MUTEX - ARCHIVO
		{
			res = FLAME_writeAux(path, buf, size, offset, fi);
		}
		dejoDeTrabajarEnPath((char*) path); //UNLOCK MUTEX - ARCHIVO
	}

	return res;
}

static int32_t FLAME_rename(const char* pathViejo, const char* pathNuevo) {
	int32_t res;

	if (strcmp((char*) pathViejo, "/") != 0
			&& strcmp((char*) pathNuevo, "/") != 0) {
		empiezoATrabajarEnPath((char*) pathViejo); //LOCK MUTEX - ARCHIVO VIEJO
		{
			empiezoATrabajarEnPath((char*) pathNuevo); //LOCK MUTEX - ARCHIVO NUEVO
			{
				char* rutaDirVieja;
				parsearDir((char*) pathViejo, &rutaDirVieja);
				empiezoATrabajarEnPath(rutaDirVieja); //LOCK MUTEX - DIRECTORIO
				{
					char* rutaDirNueva;
					parsearDir((char*) pathNuevo, &rutaDirNueva);
					empiezoATrabajarEnPath(rutaDirNueva); //LOCK MUTEX - DIRECTORIO
					{
						res = FLAME_renameAux(pathViejo, pathNuevo);
					}
					dejoDeTrabajarEnPath(rutaDirNueva); //UNLOCK MUTEX - DIRECTORIO
				}
				dejoDeTrabajarEnPath(rutaDirVieja); //UNLOCK MUTEX - DIRECTORIO
			}
			dejoDeTrabajarEnPath((char*) pathNuevo); //UNLOCK MUTEX - ARCHIVO NUEVO
		}
		dejoDeTrabajarEnPath((char*) pathViejo); //UNLOCK MUTEX - ARCHIVO VIEJO
	} else if (strcmp((char*) pathViejo, "/") == 0
			&& strcmp((char*) pathNuevo, "/") != 0) {
		empiezoATrabajarEnPath((char*) pathViejo); //LOCK MUTEX - ARCHIVO VIEJO
		{
			empiezoATrabajarEnPath((char*) pathNuevo); //LOCK MUTEX - ARCHIVO NUEVO
			{
				char* rutaDirNueva;
				parsearDir((char*) pathNuevo, &rutaDirNueva);
				empiezoATrabajarEnPath(rutaDirNueva); //LOCK MUTEX - DIRECTORIO
				{
					res = FLAME_renameAux(pathViejo, pathNuevo);
				}
				dejoDeTrabajarEnPath(rutaDirNueva); //UNLOCK MUTEX - DIRECTORIO
			}
			dejoDeTrabajarEnPath((char*) pathNuevo); //UNLOCK MUTEX - ARCHIVO NUEVO
		}
		dejoDeTrabajarEnPath((char*) pathViejo); //UNLOCK MUTEX - ARCHIVO VIEJO
	} else if (strcmp((char*) pathViejo, "/") != 0
			&& strcmp((char*) pathNuevo, "/") == 0) {
		empiezoATrabajarEnPath((char*) pathViejo); //LOCK MUTEX - ARCHIVO VIEJO
		{
			empiezoATrabajarEnPath((char*) pathNuevo); //LOCK MUTEX - ARCHIVO NUEVO
			{
				char* rutaDirVieja;
				parsearDir((char*) pathViejo, &rutaDirVieja);
				empiezoATrabajarEnPath(rutaDirVieja); //LOCK MUTEX - DIRECTORIO
				{
					res = FLAME_renameAux(pathViejo, pathNuevo);
				}
				dejoDeTrabajarEnPath(rutaDirVieja); //UNLOCK MUTEX - DIRECTORIO
			}
			dejoDeTrabajarEnPath((char*) pathNuevo); //UNLOCK MUTEX - ARCHIVO NUEVO
		}
		dejoDeTrabajarEnPath((char*) pathViejo); //UNLOCK MUTEX - ARCHIVO VIEJO
	} else {
		//pathViejo = "/" Y pathNuevo = "/"
		empiezoATrabajarEnPath((char*) pathViejo); //LOCK MUTEX - ARCHIVO VIEJO
		{
			empiezoATrabajarEnPath((char*) pathNuevo); //LOCK MUTEX - ARCHIVO NUEVO
			{
				res = FLAME_renameAux(pathViejo, pathNuevo);
			}
			dejoDeTrabajarEnPath((char*) pathNuevo); //UNLOCK MUTEX - ARCHIVO NUEVO
		}
		dejoDeTrabajarEnPath((char*) pathViejo); //UNLOCK MUTEX - ARCHIVO VIEJO
	}
	return res;
}

static int32_t FLAME_release(const char* path, struct fuse_file_info* info) {
	int32_t res = FLAME_releaseAux(path, info);
	return res;
}

static struct fuse_operations FLAME_operations = { .access = FLAME_access,
		.open = FLAME_open, .read = FLAME_read, .create = FLAME_create,
		.readdir = FLAME_readdir, .getattr = FLAME_getattr,
		.flush = FLAME_flush, .write = FLAME_write,
		.ftruncate = FLAME_ftruncate, .rename = FLAME_rename, .release =
				FLAME_release, .mkdir = FLAME_mkdir, .rmdir = FLAME_rmdir };

int8_t ejecutar_FLAME_FUSE(void) {
	if (imprimirDebug) {
		/**/printf("Se ha empezado a ejecutar FLAME_FUSE.\n");
	}
	int8_t error = 0;
	uint32_t i;

	//INICIO LA TOMA DE COMANDOS POR CONSOLA
	char buffer[500];
	char primeraParteComando[10];
	fflush(stdin);
	memset(buffer, '\0', 500);
	fgets(buffer, 500, stdin);

	//OBTENGO LA PRIMERA PARTE DEL COMANDO
	memset(primeraParteComando, '\0', 10);
	for (i = 0;
			i < 10 && buffer[i] != '\0' && buffer[i] != '\n' && buffer[i] != ' '
					&& !error; i++) {
		primeraParteComando[i] = buffer[i];
	}
	if (i < 10) {
		primeraParteComando[i] = '\0';
	}
	if (imprimirDebug) {
		/**/printf("Se ha ingresado el comando [%s]\n.", primeraParteComando);
	}

	while (!esSalir(primeraParteComando)) {
		if (strcmp(primeraParteComando, "create") == 0) {
			//ME PIDIERON CREAR UN ARCHIVO
			printf("Se ha elegido crear un archivo.\n");

			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			fflush(stdin);
			memset(buffer, '\0', 500);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}

			if (imprimirDebug) {
				/**/
				printf("Se ha ingresado el path [%s].\n", buffer);
			}

			//SE USA LA FUNCION FLAME_create
			mode_t modo;
			struct fuse_file_info fInfo;
			int32_t resultado = FLAME_create(buffer, modo, &fInfo);
			printf("El resultado de la funcion FLAME_create es [%d].\n",
					resultado);

		} else if (strcmp(primeraParteComando, "readdir") == 0) {
			//ME PIDIERON LISTAR UN DIRECTORIO
			printf("Se ha elegido listar un directorio.\n");

			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			fflush(stdin);
			memset(buffer, '\0', 500);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}

			if (imprimirDebug) {
				/**/
				printf("Se ha ingresado el path [%s].\n", buffer);
			}

			//SE USA LA FUNCION FLAME_readdir
			void* buf;
			fuse_fill_dir_t filler;
			off_t offset = 0;
			struct fuse_file_info fi;
			int32_t resultado = FLAME_readdir(buffer, buf, filler, offset, &fi);
			printf("El resultado de la funcion FLAME_readdir es [%d].\n",
					resultado);

		} else if (strcmp(primeraParteComando, "mkdir") == 0) {
			//ME PIDIERON CREAR UN DIRECTORIO
			printf("Se ha elegido crear un directorio.\n");

			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			fflush(stdin);
			memset(buffer, '\0', 500);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}

			if (imprimirDebug) {
				/**/
				printf("Se ha ingresado el path [%s].\n", buffer);
			}

			//SE USA LA FUNCION FLAME_mkdir
			mode_t modo;
			int32_t resultado = FLAME_mkdir(buffer, modo);
			printf("El resultado de la funcion FLAME_mkdir es [%d].\n",
					resultado);

		} else if (strcmp(primeraParteComando, "unlink") == 0) {
			//ME PIDIERON ELIMINAR UN ARCHIVO
			printf("Se ha elegido eliminar un archivo.\n");

			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			fflush(stdin);
			memset(buffer, '\0', 500);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}

			if (imprimirDebug) {
				/**/
				printf("Se ha ingresado el path [%s].\n", buffer);
			}

			//SE USA LA FUNCION FLAME_unlink
			int32_t resultado = FLAME_unlink(buffer);
			printf("El resultado de la funcion FLAME_unlink es [%d].\n",
					resultado);

		} else if (strcmp(primeraParteComando, "getattr") == 0) {
			//ME PIDIERON ATRIBUTOS DE UN ARCHIVO
			printf("Se ha elegido pedir atributos de un archivo.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_getattr
			struct stat stbuf;
			int32_t resultado = FLAME_getattr(buffer, &stbuf);
			printf("El resultado de la funcion FLAME_getattr es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "release") == 0) {
			//ME PIDIERON LIBERAR UN ARCHIVO ABIERTO
			printf("Se ha elegido liberar un archivo abierto.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_release
			struct fuse_file_info fi;
			int32_t resultado = FLAME_release(buffer, &fi);
			printf("El resultado de la funcion FLAME_release es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "read") == 0) {
			//ME PIDIERON LEER UN ARCHIVO ABIERTO
			printf("Se ha elegido leer un archivo abierto.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_read
			struct fuse_file_info fi;

			size_t size;
			printf("Ingreso el tamanio de los datos a leer.");
			scanf("%d", &size);
			uint32_t offset;
			printf("Ingreso desde donde leer el archivo.");
			fflush(stdin);
			scanf("%d", &offset);
			char* buf = (char*) malloc(size);

			int32_t resultado = FLAME_read(buffer, buf, size, (off_t) offset,
					&fi);
			printf("El resultado de la funcion FLAME_read es [%d].\n",
					resultado);
			for (i = 0; i < resultado; i++) {
				printf("caracter [%d] :: [%d].\n", i, buf[i]);
			}
			free(buf);
		} else if (strcmp(primeraParteComando, "write") == 0) {
			//ME PIDIERON ESCRIBIR UN ARCHIVO ABIERTO
			printf("Se ha elegido escribir un archivo abierto.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_write
			struct fuse_file_info fi;

			size_t size;
			printf("Ingreso el tamanio de los datos a escribir.");
			fflush(stdin);
			scanf("%d", &size);
			uint32_t offset;
			printf("Ingreso desde donde escribo el archivo.");
			fflush(stdin);
			scanf("%d", &offset);

			/**/
			printf("Valor del offset: [%d].\n", offset);

			char* buf = (char*) malloc(size);
			memset(buf, '\0', size);
			for (i = 0; i < size; i++) {
				buf[i] = 'b';
			}
			printf("\n");

			int32_t resultado = FLAME_write(buffer, buf, size, (off_t) offset,
					&fi);
			printf("El resultado de la funcion FLAME_write es [%d].\n",
					resultado);
			free(buf);
		} else if (strcmp(primeraParteComando, "flush") == 0) {
			//ME PIDIERON HACER FLUSH DE LA CACHE
			printf("Se ha elegido flushear la cache.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_flush
			struct fuse_file_info fi;
			int32_t resultado = FLAME_flush(buffer, &fi);
			printf("El resultado de la funcion FLAME_flush es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "open") == 0) {
			//ME PIDIERON ABRIR UN ARCHIVO
			printf("Se ha elegido abrir un archivo.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_open
			struct fuse_file_info fi;
			int32_t resultado = FLAME_open(buffer, &fi);
			printf("El resultado de la funcion FLAME_open es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "rmdir") == 0) {
			//ME PIDIERON BORRAR UN DIRECTORIO
			printf("Se ha elegido borrar un directorio.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_rmdir
			struct fuse_file_info fi;
			int32_t resultado = FLAME_rmdir(buffer);
			printf("El resultado de la funcion FLAME_rmdir es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "rename") == 0) {
			//ME PIDIERON RENOMBRAR UN ARCHIVO
			printf("Se ha elegido renombrar un archivo.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path viejo: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");

			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}

			char buffer2[500];
			printf("Ingrese el path nuevo: ");
			memset(buffer2, '\0', 500);
			fflush(stdin);
			fgets(buffer2, 500, stdin);
			printf("\n");

			/**/
			printf("Se ingreso el path: [%s].\n", buffer);

			for (i = 0; i < 500 && buffer2[i] != '\0'; i++) {
				if (buffer2[i] == '\n') {
					buffer2[i] = '\0';
				}
			}

			/**/
			printf("Quedo el path: [%s].\n", buffer);
			//SE USA LA FUNCION FLAME_rename
			int32_t resultado = FLAME_rename(buffer, buffer2);
			printf("El resultado de la funcion FLAME_rename es [%d].\n",
					resultado);
		} else if (strcmp(primeraParteComando, "ftruncate") == 0) {
			//ME PIDIERON TRUNCAR UN ARCHIVO
			printf("Se ha elegido borrar un archivo.\n");
			//SE PIDE EL PATH
			printf("Ingrese el path: ");
			memset(buffer, '\0', 500);
			fflush(stdin);
			fgets(buffer, 500, stdin);
			printf("\n");
			uint32_t tamanio;
			printf("Cantidad de bytes a truncar el archivo");
			fflush(stdin);
			scanf("%d", &tamanio);
			struct fuse_file_info fi;
			uint16_t i;
			for (i = 0; i < 500 && buffer[i] != '\0'; i++) {
				if (buffer[i] == '\n') {
					buffer[i] = '\0';
				}
			}
			//SE USA LA FUNCION FLAME_ftruncate
			int32_t resultado = FLAME_ftruncate(buffer, tamanio, &fi);
			printf("El resultado de la funcion FLAME_ftruncate es [%d].\n",
					resultado);

		} else if (strcmp(primeraParteComando, "salir") == 0) {
			printf("Se ha elegido salir del FLAME_FUSE.\n");
		} else
			printf("El comando no es válido.\n");

		error = 0;
		//VUELVO A PEDIR EL COMANDO
		fflush(stdin);
		fflush(stdout);
		memset(buffer, '\0', 500);
		fgets(buffer, 200, stdin);

		//OBTENGO LA PRIMERA PARTE DEL COMANDO
		memset(primeraParteComando, '\0', 10);
		for (i = 0;
				i < 10 && buffer[i] != '\0' && buffer[i] != '\n'
						&& buffer[i] != ' ' && !error; i++) {
			primeraParteComando[i] = buffer[i];
		}
		if (i < 10) {
			primeraParteComando[i] = '\0';
		}
		printf("Se ha ingresado el comando [%s]\n.", primeraParteComando);
	}
	return 0;
}

void iniciarFUSE(t_argFUSE* argFUSE) {
	uint32_t i;
	if (imprimirDebug) {
		for (i = 0; (*argFUSE).argv[i]; i++) {
			printf("[%d][%s].\n", i, (*argFUSE).argv[i]);
		}
	}
	if (usarFUSE) {
		(*(*argFUSE).res) =
				fuse_main((*argFUSE).argc, (*argFUSE).argv, &FLAME_operations, NULL);
	} else {
		(*(*argFUSE).res) = ejecutar_FLAME_FUSE();
	}
}

void main(int32_t argc, char** argv) {

	printf("============================================================\n");
	printf("              ______ _       ___ ___  ________              \n");
	printf("              |  ___| |     / _ \\|  \\/  |  ___|           \n");
	printf("              | |_  | |    / /_\\ \\ .  . | |__             \n");
	printf("              |  _| | |    |  _  | |\\/| |  __|             \n");
	printf("              | |   | |____| | | | |  | | |___              \n");
	printf("              \\_|   \\_____/\\_| |_|_|  |_|____/           \n");
	printf("                                                            \n");
	printf("============================================================\n");
	printf("                     PROCESO FILE SYSTEM                    \n");
	printf("============================================================\n");
	printf("\n\n");

	parsearArgV(&argv);

	if (!usarPoolConexiones) {
		sem_init(&semConexiones, 0, MAX_CONEXIONES);
	}

	int8_t error;
	uint32_t i;

	pCacheFte = NULL;
	lSincro = NULL;

	signal(SIGUSR1, crearInformeCache);

	//LEO EL ARCHIVO DE CONFIGURACION
	error = leerArchivoConfiguracion();

	while (error) {
		printf("Error %d al leer el archivo de configuracion.\n", error);
		printf("Revise el archivo y presione enter para continuar.\n");
		getchar();
		error = leerArchivoConfiguracion();
	}

	if (imprimirDebug) {
		printf(
				"Se utilizara lo siguiente para la conexion; IP: %s /Puerto: %s\n",
				IP_ACTIVA, PUERTO_ACTIVO);
	}

	//SOCKETS
	//INICIALIZO EL POOL DE CONEXIONES
	error = inicializarPoolConexiones();

	if (!error) {

		//LEO EL BOOT SECTOR
		error = leerBootSector();
		if (!error) {
			printf(
					"============================================================\n");
			printf(
					"                 CONTENIDO DEL BOOT SECTOR                 \n");
			printf(
					"============================================================\n");
			printf("La instruccion de salto es: %c%c%c\n",
					bootSector.jumpInstruction[0],
					bootSector.jumpInstruction[1],
					bootSector.jumpInstruction[2]);
			printf("El OEM Name es: %c%c%c%c%c%c%c%c\n", bootSector.OEMName[0],
					bootSector.OEMName[1], bootSector.OEMName[2],
					bootSector.OEMName[3], bootSector.OEMName[4],
					bootSector.OEMName[5], bootSector.OEMName[6],
					bootSector.OEMName[7]);
			printf("La cantidad de bytes por sector es: %d\n",
					bootSector.bytesSector);
			printf("La cantidad de sectores por Cluster es: %d\n",
					bootSector.sectoresCluster);
			printf("La cantidad de sectores reservados es: %d\n",
					bootSector.sectoresReservados);
			printf("La cantidad de tablas FAT es: %d\n",
					bootSector.cantidadTablasFat);
			printf(
					"La cantidad de entradas de directorio raiz (12-16) es: %d\n",
					bootSector.rootDirectoryEntries);
			printf("La cantidad total de sectores es: %d\n",
					bootSector.sectoresTotales);
			printf("El media descriptor es %d\n", bootSector.mediaDescriptor);
			printf("La cantidad de sectores por tabla FAT(12-16) es: %d\n",
					bootSector.sectoresPorFat_12_16);
			printf("La cantidad de sectores por pista es: %d\n",
					bootSector.sectoresPorPista);
			printf("La cantidad de cabezas por disco es: %d\n",
					bootSector.cantidadCabezas);
			printf("La cantidad de sectores ocultos es: %d\n",
					bootSector.sectoresOcultos);
			printf("La cantidad total de sectores es: %d\n",
					bootSector.sectoresTotales2);
			printf("La cantidad de sectores por tabla FAT es: %d\n",
					bootSector.sectoresPorFat);
			printf("Mirroring Flags: %d\n", bootSector.FatFlags);
			printf("La version es: %d\n", bootSector.version);
			printf(
					"El numero de cluster del inicio del directorio raiz es: %d\n",
					bootSector.rootDirectoryCluster);
			printf("El numero de sector del FS Information Sector es: %d\n",
					bootSector.sectorFSInformationSector);
			printf(
					"El primer sector de la copia de los tres boot sectors de la FAT es: %d\n",
					bootSector.sectorInicioCopyBootSector);
			printf("Reserved: ");
			for (i = 0; i < 12; i++) {
				printf("%c", bootSector.reserved[i]);
			}
			printf("\n");
			printf("Physical Drive Number: %d\n",
					bootSector.physicalDriveNumber);
			printf("Reserved: %d\n", bootSector.reserved2);
			printf("Extended boot signature: %d\n",
					bootSector.extendedBootSignature);
			printf("Volume ID: %d\n", bootSector.volumeId);
			printf("Volume Label: ");
			for (i = 0; i < 11; i++) {
				printf("%c", bootSector.volumeLabel[i]);
			}
			printf("\n");
			printf("File System Type: ");
			for (i = 0; i < 8; i++) {
				printf("%c", bootSector.FSType[i]);
			}
			printf("\n");
			printf("Operating System Boot Code:\n");
			for (i = 0; i < 420;) {
				int8_t j;
				for (j = 0; j < 50 && i < 420; j++, i++) {
					if (bootSector.OperatingSystemBootCode[i] == '\n') {
						j = -1;
					}
					printf("%c", bootSector.OperatingSystemBootCode[i]);
				}
				printf("\n");
			}
			printf("Boot sector signature: %d\n",
					bootSector.bootSectorSignature);
			printf(
					"============================================================\n");
			printf("\n\n");

			//LEO LA FAT REGION
			error = leerFatRegion();

			if (!error) {
				//INICIO EL FUSE

				int32_t resultadoFUSE = 0;

				pthread_t tid;

				t_argFUSE argFUSE;
				argFUSE.argc = argc;
				argFUSE.argv = (char**) malloc((argc + 1) * sizeof(char*));
				for (i = 0; argv[i]; i++) {
					if (imprimirDebug) {
						printf("[%d][%s].\n", i, argv[i]);
					}
					//argFUSE.argv[i] = argv[i];
					argFUSE.argv[i] = (char*) malloc(strlen(argv[i]) + 1);
					memset(argFUSE.argv[i], '\0', strlen(argv[i]) + 1);
					strcpy(argFUSE.argv[i], argv[i]);
				}
				argFUSE.argv[i] = NULL;
				argFUSE.res = &resultadoFUSE;

				pthread_create(&tid, NULL, (void*) &iniciarFUSE, &argFUSE);

				if (!usarFUSE) {
					pthread_join(tid, NULL);
				}

				if (imprimirDebug) {
					printf("Se pudo iniciar el FUSE? [%d].\n", resultadoFUSE);
				}
				//SI SE PUDO INICIAR EL FUSE
				if (resultadoFUSE == 0) {
					error = 0;

					printf(
							"============================================================\n");
					printf(
							"                          CONSOLA                          \n");
					printf(
							"============================================================\n");

					//INICIO LA TOMA DE COMANDOS POR CONSOLA
					char comando[200];
					char primeraParteComando[10];
					printf("Ingrese el comando: ");
					fflush(stdin);
					fgets(comando, 200, stdin);

					//ELIMINO EL \n
					for (i = 0; i < 200; i++) {
						if (comando[i] == '\n')
							comando[i] = '\0';
					}

					//OBTENGO LA PRIMERA PARTE DEL COMANDO
					for (i = 0;
							comando[i] != '\0' && comando[i] != '\n'
									&& comando[i] != ' ' && !error; i++) {
						if (i < 9)
							primeraParteComando[i] = comando[i];
						else
							error = -1;
					}
					while (error) {
						error = 0;
						//MIENTRAS EL COMANDO INGRESADO NO SEA VALIDO
						printf("El comando no es válido.\n");
						printf(
								"Ingrese \"ayuda\" para ver los comandos validos.\n\n\n");
						printf("Ingrese el comando: ");
						fflush(stdin);
						fgets(comando, 200, stdin);

						//ELIMINO EL \n
						for (i = 0; i < 200; i++) {
							if (comando[i] == '\n')
								comando[i] = '\0';
						}

						//OBTENGO LA PRIMERA PARTE DEL COMANDO
						for (i = 0;
								comando[i] != '\0' && comando[i] != '\n'
										&& comando[i] != ' ' && !error; i++) {
							if (i < 9)
								primeraParteComando[i] = comando[i];
							else
								error = -1;
						}
					}
					primeraParteComando[i] = '\0';

					while (!esSalir(primeraParteComando)) {
						if (esFsinfo(primeraParteComando)) {
							printf(
									"============================================================\n");
							printf(
									"                  FILE SYSTEM INFORMATION                  \n");
							printf(
									"============================================================\n");
							printf("Cantidad de clusters ocupados: %d.\n",
									cantidadClustersOcupados());
							printf("Cantidad de clusters libres: %d.\n",
									cantidadClustersLibres());
							printf("Tamaño de los sectores: %d bytes.\n",
									bootSector.bytesSector);
							printf(
									"Tamaño de los clusters: %d bytes.\n",
									(bootSector.sectoresCluster
											* bootSector.bytesSector));
							printf(
									"Tamaño de la tabla FAT: %d kilobytes.\n",
									((bootSector.sectoresPorFat
											* bootSector.bytesSector) / 1024));
							printf(
									"============================================================\n");
							printf("\n\n");
						} else if (esFinfo(primeraParteComando)) {
							error = 0;
							//CALCULO DONDE EMPIEZA EL PATH
							for (i = 0;
									comando[i] != ' ' && comando[i] != '/'
											&& i < 200; i++)
								;

							if (imprimirDebug) {
								//IMPRIMO PARA TESTEAR
								printf("[%c]\n", comando[i]);
							}

							//SI EL INICIO DEL PATH ESTA CORRECTO
							if (i < 199 && comando[i] == ' '
									&& comando[i + 1] == '/') {
								//COMO HAY UN ESPACIO NO LO PASO
								i++;

								if (imprimirDebug) {
									//IMPRIMO PARA TESTEAR
									printf("[%c]\n", comando[i]);
									uint32_t j = i;
									printf("[");
									for (; comando[j] != '\0'; j++) {
										printf("%c", comando[j]);
									}
									printf("]\n");
								}

								infoArchivoODirectorio info;
								div_t division = div(
										bootSector.rootDirectoryCluster, 256);
								info.clusterHighTwoBytes = division.quot;
								info.clusterLowTwoBytes = division.rem;
								info.attributes = 16;
								error = buscarInfoPathDesdeRaiz(comando + i,
										&info);

								//SI NO HUBO ERRORES
								if (!error) {
									//SI LO QUE OBTUBE ES UN ARCHIVO
									if (esArchivo(info.attributes)) {
										//OBTENGO LOS CLUSTERS CORRESPONDIENTES
										nodoCluster* cClusterFte = NULL;
										nodoCluster* cClusterFin = NULL;
										uint32_t numCluster = deInfoANumCluster(
												info);

										if (numCluster != 0) {
											error = obtenerCadenaClusters(
													numCluster, &cClusterFte,
													&cClusterFin);
										}

										//SI NO HUBO ERRORES
										if (!error) {
											printf(
													"============================================================\n");
											printf(
													"                      FILE INFORMATION                      \n");
											printf(
													"============================================================\n");
											printf("Path: %s\n", comando + i);
											printf(
													"Clusters correspondientes al archivo ");
											for (i = 0;
													i < 13
															&& info.name[i]
																	!= '\0';
													i++)
												printf("%c", info.name[i]);
											if (info.extension[0] != '\0') {
												printf(".");
												for (i = 0;
														i < 3
																&& info.extension[i]
																		!= '\0';
														i++)
													printf("%c",
															info.extension[i]);
											}
											printf(":\n");
											if (numCluster == 0) {
												printf(
														"Este archivo no tiene clusters asignados.\n");
											} else {
												for (i = 0; cClusterFte != NULL;
														i++) {
													desencolarCluster(
															&cClusterFte,
															&cClusterFin,
															&numCluster);
													if (i < 20)
														printf(
																"Cluster %d: [%d]\n",
																i, numCluster);
												}
											}
											printf(
													"============================================================\n");
											printf("\n\n");
										} else if (imprimirDebug) {
											/**/printf("Error %d.\n", error);
										}

									} else
										printf(
												"El path no corresponde a un archivo.\n\n\n");
								} else
									printf(
											"No se encontro el path solicitado.\n\n\n");
							}
						} else if (esInfoCache(primeraParteComando)) {
							printf(
									"============================================================\n");
							printf(
									"           CREANDO INFORME DEL ESTADO DE LA CACHE           \n");
							printf(
									"============================================================\n");
							printf("\n\n");
							crearInformeCache(0);
						} else if (esAyuda(primeraParteComando)) {
							printf(
									"============================================================\n");
							printf(
									"                           AYUDA                           \n");
							printf(
									"============================================================\n");
							printf("Comandos validos:\n");
							printf("     fsinfo\n");
							printf("          Informacion del File System.\n");
							printf("     finfo path\n");
							printf(
									"          Informacion del archivo correspondiente al path.\n");
							printf("     infocache\n");
							printf(
									"          Imprimir un informe del estado de la cache.\n");
							printf("     ayuda\n");
							printf("          Lista de comandos validos.\n");
							printf("     salir\n");
							printf("          Salir de la consola.\n");
							printf(
									"============================================================\n");
							printf("\n\n");
						} else {
							printf("El comando no es valido.\n");
							printf(
									"Ingrese \"ayuda\" para ver los comandos validos.\n\n\n");
						}

						error = 0;
						//VUELVO A PEDIR EL COMANDO
						printf("Ingrese el comando: ");
						fflush(stdin);
						fgets(comando, 200, stdin);

						//ELIMINO EL \n
						for (i = 0; i < 200; i++) {
							if (comando[i] == '\n')
								comando[i] = '\0';
						}

						//OBTENGO LA PRIMERA PARTE DEL COMANDO
						for (i = 0;
								comando[i] != '\0' && comando[i] != '\n'
										&& comando[i] != ' ' && !error; i++) {
							if (i < 9)
								primeraParteComando[i] = comando[i];
							else
								error = -1;
						}
						while (error) {
							error = 0;
							//MIENTRAS EL COMANDO INGRESADO NO SEA VALIDO
							printf("El comando no es valido.\n");
							printf(
									"Ingrese \"ayuda\" para ver los comandos validos.\n\n\n");
							printf("Ingrese el comando: ");
							fflush(stdin);
							fgets(comando, 200, stdin);

							//ELIMINO EL \n
							for (i = 0; i < 200; i++) {
								if (comando[i] == '\n')
									comando[i] = '\0';
							}

							//OBTENGO LA PRIMERA PARTE DEL COMANDO
							for (i = 0;
									comando[i] != '\0' && comando[i] != '\n'
											&& comando[i] != ' ' && !error;
									i++) {
								if (i < 9)
									primeraParteComando[i] = comando[i];
								else
									error = -1;
							}
						}
						primeraParteComando[i] = '\0';
					}
				} else {
					//HUBO UN ERROR AL INICIAR EL FUSE
					printf("Error %d al iniciar el FUSE.\n", resultadoFUSE);
				}
				printf(
						"============================================================\n");
				printf(
						"                 FINALIZACION DE LA CONSOLA                 \n");
				printf(
						"============================================================\n");
				//TERMINA EL PROGRAMA
				if (usarFUSE) {
					pthread_join(tid, NULL);
				}
				cerrarTodasLasConexiones();
				vaciarCache();
				vaciarListaSincro();
			} else
				//HUBO UN ERROR AL LEER LA FAT REGION
				printf("Error %d al leer la Fat Region.\n", error);
		} else
			//HUBO UN ERROR AL LEER EL BOOT SECTOR
			printf("Error %d al leer el boot sector.\n", error);
	} else
//HUBO UN ERROR AL INICIALIZAR EL POOL DE CONEXIONES
		printf("Error %d al inicializar el pool de conexiones.\n", error);
}
